# [推荐系统](https://www.nosuchfield.com/2025/06/22/Recommender-Systems-in-Practice/)
from collections import defaultdict
from itertools import combinations
import math
import json

def similarity(users):
    w = defaultdict(dict)
    for u, v in combinations(users.keys(), 2):
        r1 = len(users[u] & users[v])
        r2 = math.sqrt(len(users[u]) * len(users[v]) * 1.0)
        r = r1 / r2
        w[u][v], w[v][u] = r, r  # 保存两次，方便后面使用
    return w


def recommend(user, users, w, k):
    """
    :param user: 计算指定用户的物品推荐程度
    :param users: 数据集
    :param w: 前一步计算得到的用户兴趣相似度
    :param k: 取k个兴趣最相似的用户
    :return:
    """
    rank = defaultdict(float)
    # 获取指定用户和其它用户的兴趣相似度，并按照相似度从大到小排序，取前k个数据
    for v, wuv in sorted(w[user].items(), key=lambda item: item[1], reverse=True)[:k]:
        # 取出指定用户的数据集
        for i in users[v]:
            # 如果这个数据已经在当前用户的数据集里面，跳过，因为已经感兴趣的数据不需要再次推荐
            if i in users[user]:
                continue
            rank[i] += wuv
    return rank

def main():
    users = {
        'A': {'a', 'b', 'd'},
        'B': {'a', 'c'},
        'C': {'b', 'e'},
        'D': {'c', 'd', 'e'}
    }
    for k, v in similarity(users).items():
        print(f'{k}: {json.dumps(v)}')

    rank = recommend('C', users, w, 3)
    for k, v in sorted(rank.items(), key=lambda item: item[1], reverse=True):
        print(f'{k}: {v}')